package ecoll.client.centers;

import java.util.ArrayList;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.TextBoxBase;
import com.google.gwt.user.client.ui.VerticalPanel;

import ecoll.client.Client;
import ecoll.client.database.File;
import ecoll.client.dictionary.Definition;
import ecoll.client.dictionary.Dictionary;
import ecoll.client.dictionary.WordNet;
import ecoll.shared.Constant;

public class TextProcessingCenter implements Center {

	private static final int MAX_WORD_LENGTH = 50;

	private Function uploadFile = new Function() {
		@Override
		public void execute() {

			final FormPanel form = new FormPanel();
			form.setEncoding(FormPanel.ENCODING_MULTIPART);
			form.setMethod(FormPanel.METHOD_POST);
			form.setStyleName("fileUploadForm");

			final DialogBox dialogBox = new DialogBox();
			dialogBox.setWidget(form);
			dialogBox.setText("File upload");
			dialogBox.setStyleName("fileUploadDialogBox");

			VerticalPanel holder = new VerticalPanel();

			final FileUpload upload = new FileUpload();
			upload.setName("myFile");
			holder.add(upload);

			holder.add(new HTML("<hr />"));

			DockPanel buttonPanel = new DockPanel();

			final Button submitButton = new Button("Submit");
			buttonPanel.add(submitButton, DockPanel.WEST);

			submitButton.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					if (upload.getFilename().length() == 0) {
						Window.alert("The text box must not be empty");
					} else if (!upload.getFilename().endsWith(".txt"))
						Window.alert("For now only files up to "
								+ Constant.MAX_FILE_SIZE / 1024
								+ " kB with '.txt' extension are suported!");
					else
						form.submit();
				}
			});

			Button closeButton = new Button("Close");
			closeButton.addStyleName("button");
			buttonPanel.add(closeButton, DockPanel.EAST);

			closeButton.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					dialogBox.hide();
				}
			});

			holder.add(buttonPanel);
			form.add(holder);

			Client.DATABASE_SERVICE.getBlobUrl(GWT.getModuleBaseURL()
					+ "upload", new AsyncCallback<String>() {
				@Override
				public void onSuccess(String result) {
					form.setAction(result);
				}

				@Override
				public void onFailure(Throwable caught) {
					Window.alert("Failure connecting with Blobstore!");
				}
			});

			form.addSubmitCompleteHandler(new SubmitCompleteHandler() {

				@Override
				public void onSubmitComplete(SubmitCompleteEvent event) {
					dialogBox.hide();
					Window.alert("File uploaded successfully!\n");
					useFile.execute();
				}
			});

			dialogBox.center();
		}

		@Override
		public String name() {
			return "Upload a file";
		}

	};

	private Function useFile = new Function() {

		@Override
		public void execute() {
			Client.DATABASE_SERVICE
					.getFiles(new AsyncCallback<ArrayList<File>>() {
						@Override
						public void onSuccess(ArrayList<File> result) {
							VerticalPanel fileList = new VerticalPanel();
							ScrollPanel filePanel = new ScrollPanel(fileList);
							fileList.setWidth("100%");

							setFileListingArea(fileList, result);

							Client.updateDashboard(filePanel);
						}

						@Override
						public void onFailure(Throwable caught) {
							Window.alert("Failure fetching files!");
						}

					});

		}

		@Override
		public String name() {
			return "Use an uploaded file";
		}
	};

	@Override
	public String name() {
		return "Text Processing";
	}

	@Override
	public void execute() {
	}

	@Override
	public Function function(int i) {
		if (i < 0 || i > FUNCTION.length)
			return null;
		return FUNCTION[i];
	}

	private final Function[] FUNCTION = new Function[] { uploadFile, useFile };

	private void setFileListingArea(Panel fileList, ArrayList<File> result) {
		for (int i = 0; i < result.size(); ++i) {
			final File file = result.get(i);
			DisclosurePanel fileInfo = new DisclosurePanel();
			if (i % 2 == 1)
				fileInfo.setStyleName("evenLabel");
			else
				fileInfo.setStyleName("oddLabel");

			Label fileName = new Label(file.getName());
			fileName.setStyleName("fileName");
			fileInfo.setHeader(fileName);

			HorizontalPanel hiddenInfo = new HorizontalPanel();
			Label label = new Label("    Size " + file.getSize() / 1024
					+ "kB.    Modified " + file.getDate() + ".    ");
			hiddenInfo.add(label);
			hiddenInfo.setCellVerticalAlignment(label,
					HasAlignment.ALIGN_MIDDLE);

			Button showFile = new Button(file.getRights().equals("r") ? "View"
					: "Edit");
			showFile.addStyleName("button");
			showFile.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					HorizontalPanel hp = new HorizontalPanel();
					setTextProcessingArea(hp, file);
					Client.updateDashboard(hp);
				}
			});
			hiddenInfo.add(showFile);

			Button testFile = new Button("Test");
			testFile.addStyleName("button");
			testFile.addClickHandler(new ClickHandler() {

				@Override
				public void onClick(ClickEvent event) {
					VerticalPanel vp = new VerticalPanel();
					setTestingArea(vp, file);
					Client.updateDashboard(vp);
				}
			});
			hiddenInfo.add(testFile);

			if (file.getRights().equals("o")) {
				Button shareFile = new Button("Share");
				shareFile.addStyleName("button");
				shareFile.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						VerticalPanel holder = new VerticalPanel();
						holder.setStyleName("fileUploadForm");

						final DialogBox dialogBox = new DialogBox();
						dialogBox.setWidget(holder);
						dialogBox.setText("File sharing");
						dialogBox.setStyleName("fileUploadDialogBox");

						HorizontalPanel sharePanel = new HorizontalPanel();

						final TextBox userBox = new TextBox();
						userBox.setText("login");
						userBox.addClickHandler(new ClickHandler() {
							@Override
							public void onClick(ClickEvent event) {
								userBox.setText("");
							}
						});
						sharePanel.add(userBox);

						final ListBox rightsBox = new ListBox();
						rightsBox.addItem("read");
						rightsBox.addItem("write");
						rightsBox.addItem("owner");
						rightsBox.setVisibleItemCount(1);
						sharePanel.add(rightsBox);

						holder.add(sharePanel);

						holder.add(new HTML("<hr />"));

						DockPanel buttonPanel = new DockPanel();

						final Button shareButton = new Button("Share");
						buttonPanel.add(shareButton, DockPanel.WEST);

						shareButton.addClickHandler(new ClickHandler() {
							@Override
							public void onClick(ClickEvent event) {
								if (userBox.getText().length() == 0) {
									Window.alert("The text box must not be empty");
									return;
								}
								int idx = rightsBox.getSelectedIndex();
								String fullRights = rightsBox.getItemText(idx);
								String rights = fullRights.substring(0, 1);
								Client.DATABASE_SERVICE.shareFile(file.getId(),
										userBox.getText(), rights,
										new AsyncCallback<Void>() {

											@Override
											public void onFailure(Throwable e) {
												Window.alert("File sharing failed: "
														+ e.getMessage());
											}

											@Override
											public void onSuccess(Void result) {
												dialogBox.hide();
												Window.alert("File shared successfully!");
											}
										});

							}
						});

						Button closeButton = new Button("Close");
						closeButton.addStyleName("button");
						buttonPanel.add(closeButton, DockPanel.EAST);

						closeButton.addClickHandler(new ClickHandler() {
							@Override
							public void onClick(ClickEvent event) {
								dialogBox.hide();
							}
						});

						holder.add(buttonPanel);

						dialogBox.center();
					}
				});
				hiddenInfo.add(shareFile);
			}

			fileInfo.setContent(hiddenInfo);

			fileList.add(fileInfo);
		}
	}

	private void setTestingArea(VerticalPanel area, File file) {
		final HTML scoreLabel = new HTML("<h3>Your score: 0/0</h3>");
		scoreLabel.setStyleName("panelHeader");
		area.add(scoreLabel);

		final VerticalPanel testArea = new VerticalPanel();
		area.add(testArea);
		testArea.setWidth("100%");
		Client.fillArea(area);

		final long id = file.getId();

		Client.DATABASE_SERVICE.getDefinitions(id,
				new AsyncCallback<ArrayList<Definition>>() {

					@Override
					public void onFailure(Throwable caught) {
						Window.alert("Operation unsuccessful! If you're authorized to perform it, try again!");
					}

					@Override
					public void onSuccess(ArrayList<Definition> definitions) {

						addNewTest(testArea, definitions, scoreLabel, 0, 0);
					}
				});
	}

	private void addNewTest(final CellPanel area,
			final ArrayList<Definition> defs, final HTML scoreLabel,
			final int score, final int tests) {

		final HorizontalPanel testPanel = new HorizontalPanel();
		if (tests % 2 == 1)
			testPanel.setStyleName("evenLabel");
		else
			testPanel.setStyleName("oddLabel");

		int nr = Random.nextInt(defs.size());
		final Definition definition = defs.get(nr);
		final int type = (!definition.getSynonyms().get(0).equals("")) ? Random
				.nextInt(2) : 0;

		Label command = new Label();
		if (type == 0)
			command = new Label("Provide the word: " + definition.getMeaning());
		else if (type == 1)
			command = new Label("Provide a synonym: " + definition.getWord()
					+ " (" + definition.getMeaning() + ")");
		testPanel.add(command);

		final TextBox answerBox = new TextBox();
		answerBox.setWidth("100%");
		answerBox.setStyleName("");
		testPanel.add(answerBox);
		testPanel.setCellWidth(answerBox, "150px");
		testPanel.setCellHorizontalAlignment(answerBox,
				HasAlignment.ALIGN_RIGHT);

		final Button send = new Button("Send");
		send.addStyleName("button");
		send.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				send.setEnabled(false);
				answerBox.setEnabled(false);
				String answer = answerBox.getText();
				if (type == 0)
					answerBox.setText(definition.getWord());
				else if (type == 1)
					answerBox.setText(definition.getSynonyms().toString());
				int res = 0;
				if ((type == 0 && answer.equals(definition.getWord()))
						|| (type == 1)
						&& (definition.getSynonyms().contains(answer))) {
					res = 1;
					send.setText("OK");
				} else {
					send.setText("Wrong");
					send.addStyleName("wrongAnswer");
				}
				scoreLabel.setHTML("<h3>Your score: " + (score + res) + "/"
						+ (tests + 1) + "</h3>");

				addNewTest(area, defs, scoreLabel, score + res, tests + 1);
			}
		});
		testPanel.add(send);
		testPanel.setCellWidth(send, "100px");
		testPanel.setCellHorizontalAlignment(send, HasAlignment.ALIGN_CENTER);

		area.add(testPanel);
		area.setCellVerticalAlignment(testPanel, HasAlignment.ALIGN_MIDDLE);
	}

	private void setTextProcessingArea(HorizontalPanel hp, File file) {
		final long id = file.getId();

		final TextArea textArea = new TextArea();
		getFile(file.getBlobKey(), textArea);
		textArea.setReadOnly(true);
		textArea.setStyleName("textArea");
		hp.add(textArea);
		hp.setCellWidth(textArea, "65%");
		textArea.setSize("100%", "95%");

		final VerticalPanel defArea = new VerticalPanel();
		setAllTranslationsArea(defArea, id);
		hp.add(defArea);
		hp.setCellWidth(defArea, "35%");
		hp.setCellHorizontalAlignment(defArea,
				HasHorizontalAlignment.ALIGN_CENTER);
		defArea.setSize("80%", "100%");

		textArea.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				int start = textArea.getCursorPos();
				int len = textArea.getSelectionLength();
				if (len > MAX_WORD_LENGTH)
					Window.alert("Selected phrase is too long!");
				else if (len > 0) {
					setTranslationArea(defArea, id, textArea.getSelectedText());
				}
				textArea.setSelectionRange(start, 0);
			}
		});
	}

	private void getFile(String blobKey, final TextBoxBase textArea) {
		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
				GWT.getModuleBaseURL() + "upload?blobKey=" + blobKey);
		try {
			builder.sendRequest(null, new RequestCallback() {
				public void onError(Request request, Throwable e) {
					Window.alert(e.getMessage());
				}

				public void onResponseReceived(Request request,
						Response response) {
					if (200 == response.getStatusCode()) {
						textArea.setText(response.getText());

					} else {
						Window.alert("Failure fetching file!");
					}
				}
			});
		} catch (RequestException e) {
			Window.alert(e.getMessage());
		}
	}

	private void setTranslationArea(final VerticalPanel defArea, final long id,
			String word) {
		defArea.clear();

		word = word.replace('\n', ' ').trim();
		HTML wordLabel = new HTML("<h3>" + word + " -  WordNet</h3>");
		defArea.add(wordLabel);

		VerticalPanel definitionBox = new VerticalPanel();
		final Dictionary dictionary = new WordNet();
		dictionary.getDefinition(word, definitionBox);
		defArea.add(definitionBox);
		defArea.setCellHeight(definitionBox, "70%");
		definitionBox.setWidth("100%");

		Button saveButton = new Button("Save");
		defArea.add(saveButton);
		defArea.add(new HTML("<br>"));

		saveButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				ArrayList<Definition> definitions = dictionary
						.getAllDefinitions();
				setAllTranslationsArea(defArea, id);
				Client.DATABASE_SERVICE.putDefinitions(definitions, id,
						new AsyncCallback() {

							@Override
							public void onFailure(Throwable e) {
								Window.alert("Editing file failed: "
										+ e.getMessage());
							}

							@Override
							public void onSuccess(Object result) {
								setAllTranslationsArea(defArea, id);
							}
						});
			}
		});
	}

	private void setAllTranslationsArea(final VerticalPanel defArea, long fileId) {
		Client.DATABASE_SERVICE.getDefinitions(fileId,
				new AsyncCallback<ArrayList<Definition>>() {

					@Override
					public void onFailure(Throwable e) {
						Window.alert("Viewing definition failed: "
								+ e.getMessage());
					}

					@Override
					public void onSuccess(ArrayList<Definition> definitions) {
						defArea.clear();
						defArea.setVerticalAlignment(HasAlignment.ALIGN_TOP);

						HTML wordLabel = new HTML("<h3>Translated words:</h3>");
						defArea.add(wordLabel);

						if (definitions.size() == 0)
							return;

						String lastWord = definitions.get(0).getWord();
						String lastDefinition = null;

						for (Definition d : definitions) {
							String word = d.getWord();
							String shortClass = d.getShortClass();
							String synonyms = d.getSynonyms().toString();
							String meaning = d.getMeaning();
							String definition = '(' + shortClass + ") "
									+ synonyms + ' ' + meaning;
							if (lastWord.equals(word)) {
								if (lastDefinition == null)
									lastDefinition = definition;
								else
									lastDefinition += " / " + definition;
							} else {
								addDefinition(defArea, lastWord, lastDefinition);
								lastWord = word;
								lastDefinition = definition;
							}
						}
						addDefinition(defArea, lastWord, lastDefinition);

						Client.fillArea(defArea);
					}
				});
	}

	private void addDefinition(Panel area, String word, String definition) {
		DisclosurePanel wordPanel = new DisclosurePanel();
		// wordPanel.setStyleName("wordListItem");
		Label wordLabel = new Label(word);
		wordLabel.setStyleName("panelHeader");
		wordPanel.setHeader(wordLabel);
		Label definitionLabel = new Label(definition);
		definitionLabel.setStyleName("panelContent");
		wordPanel.setContent(definitionLabel);
		area.add(wordPanel);
	}

}
